home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1865 / 1865.xpi / chrome / adblockplus.jar / content / ui / browserWindow.js < prev    next >
Text File  |  2010-01-07  |  28KB  |  889 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Adblock Plus.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Wladimir Palant.
  18.  * Portions created by the Initial Developer are Copyright (C) 2006-2009
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *
  23.  * ***** END LICENSE BLOCK ***** */
  24.  
  25. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  26.  
  27. var RequestList = abp.RequestList;
  28.  
  29. /**
  30.  * List of event handers to be registered. For each event handler the element ID,
  31.  * event and the actual event handler are listed.
  32.  * @type Array
  33.  */
  34. let eventHandlers = [
  35.     ["abp-tooltip", "popupshowing", abpFillTooltip],
  36.     ["abp-status-popup", "popupshowing", abpFillPopup],
  37.     ["abp-toolbar-popup", "popupshowing", abpFillPopup],
  38.     ["abp-command-settings", "command", function() { abp.openSettingsDialog(); }],
  39.     ["abp-command-sidebar", "command", toggleSidebar],
  40.     ["abp-command-togglesitewhitelist", "command", function() { toggleFilter(siteWhitelist); }],
  41.     ["abp-command-togglepagewhitelist", "command", function() { toggleFilter(pageWhitelist); }],
  42.     ["abp-command-toggleobjtabs", "command", function() { abpTogglePref("frameobjects"); }],
  43.     ["abp-command-togglecollapse", "command", function() { abpTogglePref("fastcollapse"); }],
  44.     ["abp-command-toggleshowintoolbar", "command", function() { abpTogglePref("showintoolbar"); }],
  45.     ["abp-command-toggleshowinstatusbar", "command", function() { abpTogglePref("showinstatusbar"); }],
  46.     ["abp-command-enable", "command", function() { abpTogglePref("enabled"); }],
  47.     ["abp-status", "click", abpClickHandler],
  48.     ["abp-toolbarbutton", "command", function(event) { if (event.eventPhase == event.AT_TARGET) abpCommandHandler(event); }],
  49.     ["abp-toolbarbutton", "click", function(event) { if (event.eventPhase == event.AT_TARGET && event.button == 1) abpTogglePref("enabled"); }],
  50.     ["abp-image-menuitem", "command", function() { abpNode(backgroundData || nodeData); }],
  51.     ["abp-object-menuitem", "command", function() { abpNode(nodeData); }],
  52.     ["abp-frame-menuitem", "command", function() { abpNode(frameData); }],
  53.     ["abp-removeWhitelist-menuitem", "command", function() { removeWhitelist(); }]
  54. ];
  55.  
  56. /**
  57.  * Stores the current value of showintoolbar preference (to detect changes).
  58.  */
  59. let currentlyShowingInToolbar = prefs.showintoolbar;
  60.  
  61. /**
  62.  * Filter corresponding with "disable on site" menu item (set in abpFillPopup()).
  63.  * @type Filter
  64.  */
  65. let siteWhitelist = null;
  66. /**
  67.  * Filter corresponding with "disable on site" menu item (set in abpFillPopup()).
  68.  * @type Filter
  69.  */
  70. let pageWhitelist = null;
  71.  
  72. /**
  73.  * Data associated with the node currently under mouse pointer (set in abpCheckContext()).
  74.  */
  75. let nodeData = null;
  76. /**
  77.  * Data associated with the background image currently under mouse pointer (set in abpCheckContext()).
  78.  */
  79. let backgroundData = null;
  80. /**
  81.  * Data associated with the frame currently under mouse pointer (set in abpCheckContext()).
  82.  */
  83. let frameData = null;
  84.  
  85. /**
  86.  * Progress listener detecting location changes and triggering status updates.
  87.  * @type nsIWebProgress
  88.  */
  89. let progressListener = null;
  90.  
  91. /**
  92.  * Object implementing app-specific methods.
  93.  */
  94. let abpHooks = E("abp-hooks");
  95.  
  96. /**
  97.  * Window of the detached list of blockable items (might be null or closed).
  98.  * @type nsIDOMWindow 
  99.  */
  100. let detachedSidebar = null;
  101.  
  102. abpInit();
  103.  
  104. function abpInit() {
  105.     // Initialize app hooks
  106.     for each (let hook in ["init", "getBrowser", "addTab", "getContextMenu", "getToolbox", "getDefaultToolbar", "toolbarInsertBefore"])
  107.     {
  108.         let handler = abpHooks.getAttribute(hook);
  109.         if (handler)
  110.             abpHooks[hook] = new Function(handler);
  111.     }
  112.  
  113.     // Process preferences
  114.     abpReloadPrefs();
  115.  
  116.     // Copy the menu from status bar icon to the toolbar
  117.     function fixId(node)
  118.     {
  119.         if (node.nodeType != node.ELEMENT_NODE)
  120.             return node;
  121.  
  122.         if ("id" in node && node.id)
  123.             node.id = node.id.replace(/abp-status/, "abp-toolbar");
  124.  
  125.         for (var child = node.firstChild; child; child = child.nextSibling)
  126.             fixId(child);
  127.  
  128.         return node;
  129.     }
  130.     function copyMenu(to)
  131.     {
  132.         if (!to || !to.firstChild)
  133.             return;
  134.  
  135.         to = to.firstChild;
  136.         var from = E("abp-status-popup");
  137.         for (var node = from.firstChild; node; node = node.nextSibling)
  138.             to.appendChild(fixId(node.cloneNode(true)));
  139.     }
  140.     let paletteButton = abpGetPaletteButton();
  141.     copyMenu(E("abp-toolbarbutton"));
  142.     if (paletteButton != E("abp-toolbarbutton"))
  143.         copyMenu(paletteButton);
  144.  
  145.     // Palette button elements aren't reachable by ID, create a lookup table
  146.     let paletteButtonIDs = {};
  147.     if (paletteButton)
  148.     {
  149.         function getElementIds(element)
  150.         {
  151.             if (element.hasAttribute("id"))
  152.                 paletteButtonIDs[element.getAttribute("id")] = element;
  153.  
  154.             for (let child = element.firstChild; child; child = child.nextSibling)
  155.                 if (child.nodeType == Ci.nsIDOMNode.ELEMENT_NODE)
  156.                     getElementIds(child);
  157.         }
  158.         getElementIds(paletteButton);
  159.     }
  160.  
  161.     // Register event listeners
  162.     window.addEventListener("unload", abpUnload, false);
  163.     for each (let [id, event, handler] in eventHandlers)
  164.     {
  165.         let element = E(id);
  166.         if (element)
  167.             element.addEventListener(event, handler, false);
  168.  
  169.         if (id in paletteButtonIDs)
  170.             paletteButtonIDs[id].addEventListener(event, handler, false);
  171.     }
  172.  
  173.     prefs.addListener(abpReloadPrefs);
  174.     filterStorage.addFilterObserver(abpReloadPrefs);
  175.     filterStorage.addSubscriptionObserver(abpReloadPrefs);
  176.  
  177.     let browser = abpHooks.getBrowser();
  178.     browser.addEventListener("click", handleLinkClick, true);
  179.  
  180.     let dummy = function() {};
  181.     let progressListener = {
  182.         onLocationChange: abpReloadPrefs,
  183.         onProgressChange: dummy,
  184.         onSecurityChange: dummy,
  185.         onStateChange: dummy,
  186.         onStatusChange: dummy,
  187.         QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference])
  188.     };
  189.     browser.addProgressListener(progressListener);
  190.  
  191.     // Make sure we always configure keys but don't let them break anything
  192.     try {
  193.         // Configure keys
  194.         for (var key in prefs)
  195.             if (key.match(/(.*)_key$/))
  196.                 abpConfigureKey(RegExp.$1, prefs[key]);
  197.     } catch(e) {}
  198.  
  199.     // Install context menu handler
  200.     var contextMenu = abpHooks.getContextMenu();
  201.     if (contextMenu)
  202.     {
  203.         contextMenu.addEventListener("popupshowing", abpCheckContext, false);
  204.     
  205.         // Make sure our context menu items are at the bottom
  206.         contextMenu.appendChild(E("abp-removeWhitelist-menuitem"));
  207.         contextMenu.appendChild(E("abp-frame-menuitem"));
  208.         contextMenu.appendChild(E("abp-object-menuitem"));
  209.         contextMenu.appendChild(E("abp-image-menuitem"));
  210.     }
  211.  
  212.     // First run actions
  213.     if (!("doneFirstRunActions" in prefs))
  214.     {
  215.         // Don't repeat first run actions if new window is opened
  216.         prefs.doneFirstRunActions = true;
  217.  
  218.         // Show subscriptions dialog if the user doesn't have any subscriptions yet
  219.         if (prefs.lastVersion != prefs.currentVersion)
  220.             abp.runAsync(showSubscriptions);
  221.     }
  222.  
  223.     // Window-specific first run actions
  224.     if (!("doneFirstRunActions " + window.location.href in prefs))
  225.     {
  226.         // Don't repeat first run actions for this window any more
  227.         prefs["doneFirstRunActions " + window.location.href] = true;
  228.  
  229.         let lastVersion = abpHooks.getAttribute("currentVersion") || "0.0";
  230.         if (lastVersion != prefs.currentVersion)
  231.         {
  232.             abpHooks.setAttribute("currentVersion", prefs.currentVersion);
  233.             document.persist("abp-hooks", "currentVersion");
  234.  
  235.             let needInstall = (abp.versionComparator.compare(lastVersion, "0.0") <= 0);
  236.             if (!needInstall)
  237.             {
  238.                 // Before version 1.1 we didn't add toolbar icon in SeaMonkey, do it now
  239.                 needInstall = abp.versionComparator.compare(lastVersion, "1.1") < 0 &&
  240.                                             Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).ID == "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}";
  241.             }
  242.  
  243.             // Add ABP icon to toolbar if necessary
  244.             if (needInstall)
  245.                 abp.runAsync(abpInstallInToolbar);
  246.         }
  247.     }
  248.  
  249.     if ("init" in abpHooks)
  250.         abpHooks.init();
  251. }
  252.  
  253. function abpUnload()
  254. {
  255.     prefs.removeListener(abpReloadPrefs);
  256.     filterStorage.removeFilterObserver(abpReloadPrefs);
  257.     filterStorage.removeSubscriptionObserver(abpReloadPrefs);
  258.     abpHooks.getBrowser().removeProgressListener(progressListener);
  259. }
  260.  
  261. function abpReloadPrefs() {
  262.     var state = (prefs.enabled ? "active" : "disabled");
  263.     var label = abp.getString("status_" + state + "_label");
  264.  
  265.     if (state == "active")
  266.     {
  267.         let location = getCurrentLocation();
  268.         if (location && abp.policy.isWhitelisted(location.spec))
  269.             state = "whitelisted";
  270.     }
  271.  
  272.     var tooltip = E("abp-tooltip");
  273.     if (state && tooltip)
  274.         tooltip.setAttribute("curstate", state);
  275.  
  276.     var updateElement = function(element) {
  277.         if (!element)
  278.             return;
  279.  
  280.         if (element.tagName == "statusbarpanel" || element.tagName == "vbox") {
  281.             element.hidden = !prefs.showinstatusbar;
  282.  
  283.             var labelElement = element.getElementsByTagName("label")[0];
  284.             labelElement.setAttribute("value", label);
  285.         }
  286.         else
  287.             element.hidden = !prefs.showintoolbar;
  288.  
  289.         // HACKHACK: Show status bar icon instead of toolbar icon if the application doesn't have a toolbar icon
  290.         if (element.hidden && element.tagName == "statusbarpanel" && !abpHooks.getDefaultToolbar)
  291.             element.hidden = !prefs.showintoolbar;
  292.  
  293.         if (currentlyShowingInToolbar != prefs.showintoolbar)
  294.             abpInstallInToolbar();
  295.  
  296.         currentlyShowingInToolbar = prefs.showintoolbar;
  297.  
  298.         element.setAttribute("abpstate", state);
  299.     };
  300.  
  301.     var status = E("abp-status");
  302.     updateElement(status);
  303.     if (status) {
  304.         if (prefs.defaultstatusbaraction == 0)
  305.             status.setAttribute("popup", status.getAttribute("context"));
  306.         else
  307.             status.removeAttribute("popup");
  308.     }
  309.     
  310.     var button = E("abp-toolbarbutton");
  311.     updateElement(button);
  312.     if (button) {
  313.         if (button.hasAttribute("context") && prefs.defaulttoolbaraction == 0)
  314.         {
  315.             button.setAttribute("popup", button.getAttribute("context"));
  316.             button.removeAttribute("type");
  317.         }
  318.         else
  319.             button.removeAttribute("popup");
  320.     }
  321.  
  322.     updateElement(abpGetPaletteButton());
  323. }
  324.  
  325. /**
  326.  * Tests whether image manager context menu entry should be hidden with user's current preferences.
  327.  * @return Boolean
  328.  */
  329. function shouldHideImageManager()
  330. {
  331.     let result = false;
  332.     if (prefs.hideimagemanager && "@mozilla.org/permissionmanager;1" in Cc)
  333.     {
  334.         try
  335.         {
  336.             result = true;
  337.             let enumerator = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager).enumerator;
  338.             while (enumerator.hasMoreElements())
  339.             {
  340.                 let item = enumerator.getNext().QueryInterface(Ci.nsIPermission);
  341.                 if (item.type == "image" && item.capability == Ci.nsIPermissionManager.DENY_ACTION)
  342.                 {
  343.                     result = false;
  344.                     break;
  345.                 }
  346.             }
  347.         }
  348.         catch(e)
  349.         {
  350.             result = false;
  351.         }
  352.     }
  353.  
  354.     shouldHideImageManager = function() result;
  355.     return result;
  356. }
  357.  
  358. function abpConfigureKey(key, value) {
  359.     var valid = {
  360.         accel: "accel",
  361.         ctrl: "control",
  362.         control: "control",
  363.         shift: "shift",
  364.         alt: "alt",
  365.         meta: "meta"
  366.     };
  367.  
  368.     var command = E("abp-command-" + key);
  369.     if (!command)
  370.         return;
  371.  
  372.     var parts = value.split(/\s+/);
  373.     var modifiers = [];
  374.     var keychar = null;
  375.     var keycode = null;
  376.     for (var i = 0; i < parts.length; i++) {
  377.         if (parts[i].toLowerCase() in valid)
  378.             modifiers.push(parts[i].toLowerCase());
  379.         else if (parts[i].length == 1)
  380.             keychar = parts[i];
  381.         else if ("DOM_VK_" + parts[i].toUpperCase() in Ci.nsIDOMKeyEvent)
  382.             keycode = "VK_" + parts[i].toUpperCase();
  383.     }
  384.  
  385.     if (keychar || keycode) {
  386.         var element = document.createElement("key");
  387.         element.setAttribute("id", "abp-key-" + key);
  388.         element.setAttribute("command", "abp-command-" + key);
  389.         if (keychar)
  390.             element.setAttribute("key", keychar);
  391.         else
  392.             element.setAttribute("keycode", keycode);
  393.         element.setAttribute("modifiers", modifiers.join(","));
  394.  
  395.         E("abp-keyset").appendChild(element);
  396.     }
  397. }
  398.  
  399. /**
  400.  * Handles browser clicks to intercept clicks on abp: links.
  401.  */
  402. function handleLinkClick(/**Event*/ event)
  403. {
  404.     // Ignore right-clicks
  405.     if (event.button == 2)
  406.         return;
  407.  
  408.     // Search the link associated with the click
  409.     let link = event.target;
  410.     while (link && !(link instanceof Ci.nsIDOMNSHTMLAnchorElement))
  411.         link = link.parentNode;
  412.  
  413.     if (!link || !/^abp:\/*subscribe\/*\?(.*)/i.test(link.href))
  414.         return;
  415.  
  416.     // This is our link - make sure the browser doesn't handle it
  417.     event.preventDefault();
  418.     event.stopPropagation();
  419.  
  420.     // Decode URL parameters
  421.     let title = null;
  422.     let url = null;
  423.     for each (let param in RegExp.$1.split('&'))
  424.     {
  425.         let parts = param.split("=", 2);
  426.         if (parts.length == 2 && parts[0] == 'title')
  427.             title = decodeURIComponent(parts[1]);
  428.         if (parts.length == 2 && parts[0] == 'location')
  429.             url = decodeURIComponent(parts[1]);
  430.     }
  431.     if (!url || !/\S/.test(url))
  432.         return;
  433.  
  434.     // Default title to the URL
  435.     if (!title || !/\S/.test(title))
  436.         title = url;
  437.  
  438.     // Trim spaces in title and URL
  439.     title = title.replace(/^\s+/, "").replace(/\s+$/, "");
  440.     url = url.replace(/^\s+/, "").replace(/\s+$/, "");
  441.  
  442.     // Verify that the URL is valid
  443.     url = abp.makeURL(url);
  444.     if (!url)
  445.         return;
  446.     if (url.scheme != "http" && url.scheme != "https" && url.scheme != "ftp")
  447.         return;
  448.     url = url.spec;
  449.  
  450.     // Open dialog
  451.     let appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
  452.     if (appInfo.ID != "{a23983c0-fd0e-11dc-95ff-0800200c9a66}")
  453.     {
  454.         var subscription = {url: url, title: title, disabled: false, external: false, autoDownload: true};
  455.         window.openDialog("chrome://adblockplus/content/ui/subscription.xul", "_blank",
  456.                                          "chrome,centerscreen,modal", subscription);
  457.     }
  458.     else
  459.     {
  460.         // Special handling for Fennec
  461.         window.importDialog(null, "chrome://adblockplus/content/ui/fennecSubscription.xul");
  462.         initFennecSubscriptionDialog(url, title);
  463.     }
  464. }
  465.  
  466. // Finds the toolbar button in the toolbar palette
  467. function abpGetPaletteButton()
  468. {
  469.     let toolbox = (abpHooks.getToolbox ? abpHooks.getToolbox() : null);
  470.     if (!toolbox || !("palette" in toolbox) || !toolbox.palette)
  471.         return null;
  472.  
  473.     for (var child = toolbox.palette.firstChild; child; child = child.nextSibling)
  474.         if (child.id == "abp-toolbarbutton")
  475.             return child;
  476.  
  477.     return null;
  478. }
  479.  
  480. // Check whether we installed the toolbar button already
  481. function abpInstallInToolbar()
  482. {
  483.     let tb = E("abp-toolbarbutton");
  484.     if (!tb || tb.parentNode.localName == "toolbarpalette")
  485.     {
  486.         let toolbar = (abpHooks.getDefaultToolbar ? abpHooks.getDefaultToolbar() : null);
  487.         let insertBefore = (abpHooks.toolbarInsertBefore ? abpHooks.toolbarInsertBefore() : null);
  488.         if (toolbar && "insertItem" in toolbar)
  489.         {
  490.             if (insertBefore && insertBefore.parentNode != toolbar)
  491.                 insertBefore = null;
  492.  
  493.             toolbar.insertItem("abp-toolbarbutton", insertBefore, null, false);
  494.  
  495.             toolbar.setAttribute("currentset", toolbar.currentSet);
  496.             document.persist(toolbar.id, "currentset");
  497.         }
  498.     }
  499. }
  500.  
  501. /**
  502.  * Executed on first run, presents the user with a list of filter subscriptions
  503.  * and allows choosing one.
  504.  */
  505. function showSubscriptions()
  506. {
  507.     // In Fennec we might not be initialized yet
  508.     abp.init();
  509.  
  510.     // Don't annoy the user if he has a subscription already
  511.     let hasSubscriptions = filterStorage.subscriptions.some(function(subscription) subscription instanceof abp.DownloadableSubscription);
  512.     if (hasSubscriptions)
  513.         return;
  514.  
  515.     // Only show the list if this is the first run or the user has no filters
  516.     let hasFilters = filterStorage.subscriptions.some(function(subscription) subscription.filters.length);
  517.     if (hasFilters && abp.versionComparator.compare(prefs.lastVersion, "0.0") > 0)
  518.         return;
  519.  
  520.     if (!abpHooks.addTab || abpHooks.addTab("chrome://adblockplus/content/ui/tip_subscriptions.xul") === false)
  521.         window.openDialog("chrome://adblockplus/content/ui/tip_subscriptions.xul", "_blank", "chrome,centerscreen,resizable,dialog=no");
  522. }
  523.  
  524. function abpFillTooltip(event) {
  525.     if (!document.tooltipNode || !document.tooltipNode.hasAttribute("tooltip"))
  526.     {
  527.         event.preventDefault();
  528.         return;
  529.     }
  530.  
  531.     var type = (document.tooltipNode && document.tooltipNode.id == "abp-toolbarbutton" ? "toolbar" : "statusbar");
  532.     var action = parseInt(prefs["default" + type + "action"]);
  533.     if (isNaN(action))
  534.         action = -1;
  535.  
  536.     var actionDescr = E("abp-tooltip-action");
  537.     actionDescr.hidden = (action < 0 || action > 3);
  538.     if (!actionDescr.hidden)
  539.         actionDescr.setAttribute("value", abp.getString("action" + action + "_tooltip"));
  540.  
  541.     var state = event.target.getAttribute("curstate");
  542.     var statusDescr = E("abp-tooltip-status");
  543.     statusDescr.setAttribute("value", abp.getString(state + "_tooltip"));
  544.  
  545.     var activeFilters = [];
  546.     E("abp-tooltip-blocked-label").hidden = (state != "active");
  547.     E("abp-tooltip-blocked").hidden = (state != "active");
  548.     if (state == "active") {
  549.         var data = RequestList.getDataForWindow(abpHooks.getBrowser().contentWindow);
  550.         var locations = data.getAllLocations();
  551.  
  552.         var blocked = 0;
  553.         var filterCount = {__proto__: null};
  554.         for (i = 0; i < locations.length; i++) {
  555.             if (locations[i].filter && !(locations[i].filter instanceof abp.WhitelistFilter))
  556.                 blocked++;
  557.             if (locations[i].filter) {
  558.                 if (locations[i].filter.text in filterCount)
  559.                     filterCount[locations[i].filter.text]++;
  560.                 else
  561.                     filterCount[locations[i].filter.text] = 1;
  562.             }
  563.         }
  564.  
  565.         var blockedStr = abp.getString("blocked_count_tooltip");
  566.         blockedStr = blockedStr.replace(/--/, blocked).replace(/--/, locations.length);
  567.         E("abp-tooltip-blocked").setAttribute("value", blockedStr);
  568.  
  569.         var filterSort = function(a, b) {
  570.             return filterCount[b] - filterCount[a];
  571.         };
  572.         for (var filter in filterCount)
  573.             activeFilters.push(filter);
  574.         activeFilters = activeFilters.sort(filterSort);
  575.     }
  576.  
  577.     E("abp-tooltip-filters-label").hidden = (activeFilters.length == 0);
  578.     E("abp-tooltip-filters").hidden = (activeFilters.length == 0);
  579.     if (activeFilters.length > 0) {
  580.         var filtersContainer = E("abp-tooltip-filters");
  581.         while (filtersContainer.firstChild)
  582.             filtersContainer.removeChild(filtersContainer.firstChild);
  583.  
  584.         for (var i = 0; i < activeFilters.length && i < 3; i++) {
  585.             var descr = document.createElement("description");
  586.             descr.setAttribute("value", activeFilters[i] + " (" + filterCount[activeFilters[i]] + ")");
  587.             filtersContainer.appendChild(descr);
  588.         }
  589.         if (activeFilters.length > 3) {
  590.             var descr = document.createElement("description");
  591.             descr.setAttribute("value", "...");
  592.             filtersContainer.appendChild(descr);
  593.         }
  594.     }
  595. }
  596.  
  597. /**
  598.  * Retrieves the current location of the browser (might return null on failure).
  599.  */
  600. function getCurrentLocation() /**nsIURI*/
  601. {
  602.     if ("currentHeaderData" in window && "content-base" in window.currentHeaderData)
  603.     {
  604.         // Thunderbird blog entry
  605.         return abp.unwrapURL(window.currentHeaderData["content-base"].headerValue);
  606.     }
  607.     else if ("currentHeaderData" in window && "from" in window.currentHeaderData)
  608.     {
  609.         // Thunderbird mail/newsgroup entry
  610.         try
  611.         {
  612.             let headerParser = Cc["@mozilla.org/messenger/headerparser;1"].getService(Ci.nsIMsgHeaderParser);
  613.             let emailAddress = headerParser.extractHeaderAddressMailboxes(window.currentHeaderData.from.headerValue);
  614.             return abp.makeURL("mailto:" + emailAddress.replace(/^[\s"]+/, "").replace(/[\s"]+$/, "").replace(/\s/g, "%20"));
  615.         }
  616.         catch(e)
  617.         {
  618.             return null;
  619.         }
  620.     }
  621.     else
  622.     {
  623.         // Regular browser
  624.         return abp.unwrapURL(abpHooks.getBrowser().contentWindow.location.href);
  625.     }
  626. }
  627.  
  628. // Fills the context menu on the status bar
  629. function abpFillPopup(event) {
  630.     let popup = event.target;
  631.  
  632.     // Not at-target call, ignore
  633.     if (popup.getAttribute("id").indexOf("options") >= 0)
  634.         return;
  635.  
  636.     // Need to do it this way to prevent a Gecko bug from striking
  637.     var elements = {};
  638.     var list = popup.getElementsByTagName("menuitem");
  639.     for (var i = 0; i < list.length; i++)
  640.         if (list[i].id && /\-(\w+)$/.test(list[i].id))
  641.             elements[RegExp.$1] = list[i];
  642.  
  643.     var sidebarOpen = abpIsSidebarOpen();
  644.     elements.opensidebar.hidden = sidebarOpen;
  645.     elements.closesidebar.hidden = !sidebarOpen;
  646.  
  647.     var whitelistItemSite = elements.whitelistsite;
  648.     var whitelistItemPage = elements.whitelistpage;
  649.     whitelistItemSite.hidden = whitelistItemPage.hidden = true;
  650.  
  651.     var whitelistSeparator = whitelistItemPage.nextSibling;
  652.     while (whitelistSeparator.nodeType != whitelistSeparator.ELEMENT_NODE)
  653.         whitelistSeparator = whitelistSeparator.nextSibling;
  654.  
  655.     let location = getCurrentLocation();
  656.     if (location && abp.policy.isBlockableScheme(location))
  657.     {
  658.         let host = null;
  659.         try
  660.         {
  661.             host = location.host.replace(/^www\./, "");
  662.         } catch (e) {}
  663.  
  664.         if (host)
  665.         {
  666.             let ending = "|";
  667.             if (location instanceof Ci.nsIURL && location.ref)
  668.                 location.ref = "";
  669.             if (location instanceof Ci.nsIURL && location.query)
  670.             {
  671.                 location.query = "";
  672.                 ending = "?";
  673.             }
  674.  
  675.             siteWhitelist = abp.Filter.fromText("@@||" + host + "^$document");
  676.             whitelistItemSite.setAttribute("checked", siteWhitelist.subscriptions.length && !siteWhitelist.disabled);
  677.             whitelistItemSite.setAttribute("label", whitelistItemSite.getAttribute("labeltempl").replace(/--/, host));
  678.             whitelistItemSite.hidden = false;
  679.  
  680.             pageWhitelist = abp.Filter.fromText("@@|" + location.spec + ending + "$document");
  681.             whitelistItemPage.setAttribute("checked", pageWhitelist.subscriptions.length && !pageWhitelist.disabled);
  682.             whitelistItemPage.hidden = false;
  683.         }
  684.         else
  685.         {
  686.             siteWhitelist = abp.Filter.fromText("@@|" + location.spec + "|");
  687.             whitelistItemSite.setAttribute("checked", siteWhitelist.subscriptions.length && !siteWhitelist.disabled);
  688.             whitelistItemSite.setAttribute("label", whitelistItemSite.getAttribute("labeltempl").replace(/--/, location.spec.replace(/^mailto:/, "")));
  689.             whitelistItemSite.hidden = false;
  690.         }
  691.     }
  692.     whitelistSeparator.hidden = whitelistItemSite.hidden && whitelistItemPage.hidden;
  693.  
  694.     elements.enabled.setAttribute("checked", prefs.enabled);
  695.     elements.frameobjects.setAttribute("checked", prefs.frameobjects);
  696.     elements.slowcollapse.setAttribute("checked", !prefs.fastcollapse);
  697.     elements.showintoolbar.setAttribute("checked", prefs.showintoolbar);
  698.     elements.showinstatusbar.setAttribute("checked", prefs.showinstatusbar);
  699.  
  700.     var defAction = (popup.tagName == "menupopup" || document.popupNode.id == "abp-toolbarbutton" ? prefs.defaulttoolbaraction : prefs.defaultstatusbaraction);
  701.     elements.opensidebar.setAttribute("default", defAction == 1);
  702.     elements.closesidebar.setAttribute("default", defAction == 1);
  703.     elements.settings.setAttribute("default", defAction == 2);
  704.     elements.enabled.setAttribute("default", defAction == 3);
  705. }
  706.  
  707. function abpIsSidebarOpen() {
  708.     // Test whether detached sidebar window is open
  709.     if (detachedSidebar && !detachedSidebar.closed)
  710.         return true;
  711.  
  712.     var sidebar = E("abp-sidebar");
  713.     return (sidebar ? !sidebar.hidden : false);
  714. }
  715.  
  716. function toggleSidebar()
  717. {
  718.     if (detachedSidebar && !detachedSidebar.closed)
  719.     {
  720.         detachedSidebar.close();
  721.         detachedSidebar = null;
  722.     }
  723.     else
  724.     {
  725.         var sidebar = E("abp-sidebar");
  726.         if (sidebar && (!prefs.detachsidebar || !sidebar.hidden))
  727.         {
  728.             E("abp-sidebar-splitter").hidden = !sidebar.hidden;
  729.             E("abp-sidebar-browser").setAttribute("src", sidebar.hidden ? "chrome://adblockplus/content/ui/sidebar.xul" : "about:blank");
  730.             sidebar.hidden = !sidebar.hidden;
  731.             if (sidebar.hidden)
  732.                 abpHooks.getBrowser().contentWindow.focus();
  733.         }
  734.         else
  735.             detachedSidebar = window.openDialog("chrome://adblockplus/content/ui/sidebarDetached.xul", "_blank", "chrome,resizable,dependent,dialog=no");
  736.     }
  737.  
  738.     let menuItem = E("abp-blockableitems");
  739.     if (menuItem)
  740.         menuItem.setAttribute("checked", abpIsSidebarOpen());
  741. }
  742.  
  743. // Toggles the value of a boolean pref
  744. function abpTogglePref(pref) {
  745.     prefs[pref] = !prefs[pref];
  746.     prefs.save();
  747. }
  748.  
  749. /**
  750.  * If the given filter is already in user's list, removes it from the list. Otherwise adds it.
  751.  */
  752. function toggleFilter(/**Filter*/ filter)
  753. {
  754.     if (filter.subscriptions.length)
  755.     {
  756.         if (filter.disabled || filter.subscriptions.some(function(subscription) !(subscription instanceof abp.SpecialSubscription)))
  757.         {
  758.             filter.disabled = !filter.disabled;
  759.             filterStorage.triggerFilterObservers(filter.disabled ? "disable" : "enable", [filter]);
  760.         }
  761.         else
  762.             filterStorage.removeFilter(filter);
  763.     }
  764.     else
  765.         filterStorage.addFilter(filter);
  766.     filterStorage.saveToDisk();
  767. }
  768.  
  769. /**
  770.  * Removes/disable the exception rule applying for the current page.
  771.  */
  772. function removeWhitelist()
  773. {
  774.     let location = getCurrentLocation();
  775.     let filter = null;
  776.     if (location)
  777.         filter = abp.policy.isWhitelisted(location.spec);
  778.     if (filter && filter.subscriptions.length && !filter.disabled)
  779.         toggleFilter(filter);
  780. }
  781.  
  782. // Handle clicks on the Adblock statusbar panel
  783. function abpClickHandler(e) {
  784.     if (e.button == 0)
  785.         abpExecuteAction(prefs.defaultstatusbaraction);
  786.     else if (e.button == 1)
  787.         abpTogglePref("enabled");
  788. }
  789.  
  790. function abpCommandHandler(e) {
  791.     if (prefs.defaulttoolbaraction == 0)
  792.         e.target.open = true;
  793.     else
  794.         abpExecuteAction(prefs.defaulttoolbaraction);
  795. }
  796.  
  797. // Executes default action for statusbar/toolbar by its number
  798. function abpExecuteAction(action) {
  799.     if (action == 1)
  800.         toggleSidebar();
  801.     else if (action == 2)
  802.         abp.openSettingsDialog();
  803.     else if (action == 3)
  804.         abpTogglePref("enabled");
  805. }
  806.  
  807. // Retrieves the image URL for the specified style property
  808. function abpImageStyle(computedStyle, property) {
  809.     var value = computedStyle.getPropertyCSSValue(property);
  810.     if (value instanceof Ci.nsIDOMCSSValueList && value.length >= 1)
  811.         value = value[0];
  812.     if (value instanceof Ci.nsIDOMCSSPrimitiveValue && value.primitiveType == Ci.nsIDOMCSSPrimitiveValue.CSS_URI)
  813.         return abp.unwrapURL(value.getStringValue()).spec;
  814.  
  815.     return null;
  816. }
  817.  
  818. // Hides the unnecessary context menu items on display
  819. function abpCheckContext() {
  820.     var contextMenu = abpHooks.getContextMenu();
  821.     var target = document.popupNode;
  822.  
  823.     var nodeType = null;
  824.     backgroundData = null;
  825.     frameData = null;
  826.     if (target) {
  827.         // Lookup the node in our stored data
  828.         var data = RequestList.getDataForNode(target);
  829.         var targetNode = null;
  830.         if (data) {
  831.             targetNode = data[0];
  832.             data = data[1];
  833.         }
  834.         nodeData = data;
  835.         if (data && !data.filter)
  836.             nodeType = data.typeDescr;
  837.  
  838.         var wnd = (target ? target.ownerDocument.defaultView : null);
  839.         var wndData = (wnd ? RequestList.getDataForWindow(wnd) : null);
  840.  
  841.         if (wnd.frameElement)
  842.             frameData = RequestList.getDataForNode(wnd.frameElement, true);
  843.         if (frameData)
  844.             frameData = frameData[1];
  845.         if (frameData && frameData.filter)
  846.             frameData = null;
  847.  
  848.         if (nodeType != "IMAGE") {
  849.             // Look for a background image
  850.             var imageNode = target;
  851.             while (imageNode && !backgroundData) {
  852.                 if (imageNode.nodeType == imageNode.ELEMENT_NODE) {
  853.                     var bgImage = null;
  854.                     var style = wnd.getComputedStyle(imageNode, "");
  855.                     bgImage = abpImageStyle(style, "background-image") || abpImageStyle(style, "list-style-image");
  856.                     if (bgImage) {
  857.                         backgroundData = wndData.getLocation(abp.policy.type.BACKGROUND, bgImage);
  858.                         if (backgroundData && backgroundData.filter)
  859.                             backgroundData = null;
  860.                     }
  861.                 }
  862.  
  863.                 imageNode = imageNode.parentNode;
  864.             }
  865.         }
  866.  
  867.         // Hide "Block Images from ..." if hideimagemanager pref is true and the image manager isn't already blocking something
  868.         var imgManagerContext = E("context-blockimage");
  869.         if (imgManagerContext && shouldHideImageManager())
  870.         {
  871.             // Don't use "hidden" attribute - it might be overridden by the default popupshowing handler
  872.             imgManagerContext.collapsed = true;
  873.         }
  874.     }
  875.  
  876.     E("abp-image-menuitem").hidden = (nodeType != "IMAGE" && backgroundData == null);
  877.     E("abp-object-menuitem").hidden = (nodeType != "OBJECT");
  878.     E("abp-frame-menuitem").hidden = (frameData == null);
  879.  
  880.     let location = getCurrentLocation();
  881.     E("abp-removeWhitelist-menuitem").hidden = (!location || !abp.policy.isWhitelisted(location.spec));
  882. }
  883.  
  884. // Bring up the settings dialog for the node the context menu was referring to
  885. function abpNode(data) {
  886.     if (data)
  887.         window.openDialog("chrome://adblockplus/content/ui/composer.xul", "_blank", "chrome,centerscreen,resizable,dialog=no,dependent", abpHooks.getBrowser().contentWindow, data);
  888. }
  889.